package pl.edu.agh.neuraleconomy.core.ta.advice;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import pl.edu.agh.neuraleconomy.core.nn.CoreUtils;
import pl.edu.agh.neuraleconomy.core.ta.indicator.ROCCalculator;
import pl.edu.agh.neuraleconomy.model.exchange.Company;
import pl.edu.agh.neuraleconomy.model.exchange.Exchange;
import pl.edu.agh.neuraleconomy.persistence.base.DaoProvider;
import pl.edu.agh.neuraleconomy.persistence.exchange.ExchangeDao;

public class ROCAdvisor extends IndicatorAdvisor {
	private ExchangeDao dao = DaoProvider.getExchangeDao();
	private int period;

	public ROCAdvisor(int period) {
		super();
		this.period = period;
		this.calculator = new ROCCalculator(period);
	}

	public Advice getAdvice(Company company, Date date) {
		List<Exchange> data = getData(company, date);
		if (data.size() < period * 2) {
			logger.warn("Not enough data to give advice for company " + company.getName());
			return null;
		}
		double[] roc = calculator.calculate(CoreUtils.toDoubleArray(data));

		Extremum lastExtremum = lastExtremum(roc);
		double central = centralValue(roc);
		
		if(lastExtremum == null){
			return new Advice(company, AdviceType.STAY, 0);
		}

		if (lastExtremum.isMaximum() && lastExtremum.getValue() > central){
			return new Advice(company, AdviceType.SELL, 80);
		}
		
		if(!lastExtremum.isMaximum() && lastExtremum.getValue() < central){
			return new Advice(company, AdviceType.BUY, 80);
		}

		return new Advice(company, AdviceType.STAY, 0);

	}

	private List<Exchange> getData(Company c, Date d) {
		return dao.getLatestByCompanyIncludeDate(c.getId(), d, 4L * period);
	}

	private ArrayList<Double> findTops(double[] data) {
		ArrayList<Double> tops = new ArrayList<Double>();

		for (int i = 1; i < data.length - 1; i++) {
			double prev = data[i - 1];
			double curr = data[i];
			double next = data[i + 1];

			if (curr > prev && curr >= next) {
				tops.add(Double.valueOf(curr));
			}
		}

		return tops;
	}

	private ArrayList<Double> findHoles(double[] data) {
		ArrayList<Double> holes = new ArrayList<Double>();

		for (int i = 1; i < data.length - 1; i++) {
			double prev = data[i - 1];
			double curr = data[i];
			double next = data[i + 1];

			if (curr < prev && curr < next) {
				holes.add(Double.valueOf(curr));
			}
		}

		return holes;
	}

	private Extremum lastExtremum(double[] data) {

		for (int i = data.length - 2; i >= 1; i--) {
			double prev = data[i - 1];
			double curr = data[i];
			double next = data[i + 1];

			if (curr <= prev && curr < next) {
				return new Extremum(curr, false);
			}

			if (curr >= prev && curr > next) {
				return new Extremum(curr, true);
			}
		}

		return null;
	}

	private double centralValue(double[] data) {
		double max = Double.MIN_VALUE;
		double min = Double.MAX_VALUE;

		for (double d : data) {
			if (d < min) {
				min = d;
			}
			if (d > max) {
				max = d;
			}
		}

		return (max + min) / (double) 2;
	}

	@Data
	@AllArgsConstructor
	private class Extremum {
		double value;
		boolean maximum;
	}

}
